<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8"/>
    <title>Martin Debug Page</title>
    <meta name="viewport" content="initial-scale=1,width=device-width"/>
    <script src="https://unpkg.com/maplibre-gl@3.3.1/dist/maplibre-gl.js"></script>
    <link href="https://unpkg.com/maplibre-gl@3.3.1/dist/maplibre-gl.css" rel="stylesheet"/>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/randomcolor/0.6.1/randomColor.js"></script>

    <style>
        body {
            margin: 0;
            padding: 0;
        }

        #map {
            position: absolute;
            top: 0;
            bottom: 0;
            width: 100%;
        }

        #menu {
            background: #fff;
            position: absolute;
            right: 5px;
            z-index: 1;
            width: 100%;
            height: 100%;
            border-radius: 3px;
            font-family: 'Open Sans', sans-serif;
            overflow: auto;
            user-select: none;
        }

        #menu a {
            font-size: 13px;
            color: #404040;
            display: block;
            margin: 0;
            padding: 10px;
            text-decoration: none;
            border-bottom: 1px solid #0000003F;
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
        }

        #menu a:last-child {
            border: none;
        }

        #menu a:hover {
            background-color: #f8f8f8;
            color: #a00043;
        }

        #menu-search {
            padding: 2px;
            width: 100%;
        }

        #menu label {
            display: block;
            margin: 5px;
            font-weight: bold;
        }

        #container {
            position: absolute;
            top: 10px;
            bottom: 10px;
            left: 10px;
            width: 250px;
        }

        .resizer {
            position: absolute;
            cursor: col-resize;
            height: 100%;
            right: 0;
            top: 0;
            width: 5px;
            z-index: 1;
            background-color: #0000003F;
        }
    </style>

    <!-- popup styling -->
    <style>
        .maplibregl-popup-content {
            overflow-y: auto;
            padding: 0 10px;
        }

        .inspect_popup {
            color: #333;
            display: table;
        }

        .inspect_feature {
            padding: 10px 0;
        }

        .inspect_feature:not(:last-child) {
            border-bottom: 1px solid #ccc;
        }

        .inspect_layer {
            display: block;
            font-weight: bold;
        }

        .inspect_property {
            display: table-row;
        }

        .inspect_property_name {
            display: table-cell;
            padding-right: 10px;
        }

        .inspect_property_value {
            display: table-cell;
        }
    </style>
</head>

<body>
<div id="container">
    <nav id="menu">
        <label for="menu-search">Tile Sources</label>
        <input oninput="handleSearch()" id="menu-search" type="search" placeholder="Search..."/>
    </nav>
    <div class="resizer"></div>
</div>
<div id="map"></div>
<script>
    //ignore this, it's just to allow user resize menu by drag
    const container = document.getElementById('container');

    let x = 0;
    let w = 0;

    const mouseDownHandler = function (e) {
        const styles = window.getComputedStyle(container);
        x = e.clientX;
        w = parseInt(styles.width, 10);

        document.addEventListener('mousemove', mouseMoveHandler);
        document.addEventListener('mouseup', mouseUpHandler);
    };

    const mouseMoveHandler = function (e) {
        const dx = e.clientX - x;
        container.style.width = `${w + dx}px`;
    };

    container.addEventListener('mousedown', mouseDownHandler)

    const mouseUpHandler = function () {
        document.removeEventListener('mousemove', mouseMoveHandler);
        document.removeEventListener('mouseup', mouseUpHandler);
    };
</script>
<script>
    function handleSearch() {
        const search = document.getElementById("menu-search").value;
        const links = document.querySelectorAll("#menu a");
        for (const link of links) {
            if (link.textContent.toLowerCase().includes(search.toLowerCase())) {
                link.style.display = "block";
            } else {
                link.style.display = "none";
            }
        }
    }

    const string2RandColor = function (str) {
        const luminosity = "bright";
        const hues = ["pink", "blue", "orange", "monochrome", "yellow", "dark", "green"];
        let hue = hues[Math.floor(Math.random() * hues.length)];

        return randomColor({
            luminosity: luminosity,
            hue: hue,
            seed: str,
            format: 'rgbArray'
        }).join(',');
    }

    const map = new maplibregl.Map({
        container: 'map',
        style: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
        zoom: 0,
        center: [0, 0],
        hash: true
    });

    const QUERY_THRESHOLD = 10;
    const popup = new maplibregl.Popup({
        closeButton: false,
        closeOnClick: false
    });

    const renderProperty = function (pName, pValue) {
        return `<div class="inspect_property">
                  <div class="inspect_property_name">
                    ${pName}
                  </div>
                  <div class="inspect_property_value">
                    ${pValue}
                  </div>
                </div>`;
    }

    const renderProperties = function (feature) {
        const geomDiv = renderProperty("$type", feature.geometry.type);
        const propertiesDivs = Object.keys(feature.properties).map(propName => {
            return renderProperty(propName, feature.properties[propName]);
        }).join('');

        return `${geomDiv}
                ${propertiesDivs}`;
    }

    const renderFeature = function (feature) {
        const srcLayer = feature.layer['source-layer'];
        let layerName = feature.layer.source;
        if (srcLayer && srcLayer !== layerName) {
            layerName = `${layerName} / ${srcLayer}`;
        }
        return `<div class="inspect_feature">
                  <div class="inspect_layer">
                    ${layerName}
                  </div>
                  ${renderProperties(feature)}
                </div>`;
    }

    const renderFeatures = function (features) {
        const featureDOMS = features.map(feat => renderFeature(feat)).join('');
        return `<inspect_popup class="inspect_popup">
                  ${featureDOMS}
                </inspect_popup>`;
    }

    const tryShowPopup = function (e) {
        const queryBox = [
            [
                e.point.x - QUERY_THRESHOLD,
                e.point.y + QUERY_THRESHOLD
            ],
            [
                e.point.x + QUERY_THRESHOLD,
                e.point.y - QUERY_THRESHOLD
            ]
        ];

        let features = map.queryRenderedFeatures(queryBox) || [];
        features = features.filter(f => f.source !== 'carto');
        popup.setLngLat(e.lngLat);
        if (features.length !== 0) {
            const renderedPopup = renderFeatures(features);
            popup.setHTML(renderedPopup);
            popup.addTo(map);
            return true;
        } else {
            return false;
        }
    }

    let isPopupFixed = false;
    map.on("click", e => {
        //remove the popup immediately if it's benn shown already
        if (popup.isOpen()) {
            popup.remove();
            isPopupFixed = false;
        }
        isPopupFixed = tryShowPopup(e);
    });

    map.on("mousemove", function (e) {
        if (isPopupFixed) return;
        let showOk = tryShowPopup(e);
        if (!showOk) {
            popup.remove();
        }
    });

    map.on('load', async function () {
        map.showTileBoundaries = true;
        const catalog = await fetch('http://0.0.0.0:3000/catalog');
        const sources = (await catalog.json()).tiles;
        // Set up the corresponding toggle button for each layer.
        for (const sourceName of Object.keys(sources)) {
            // Skip layers that already have a button set up.
            if (document.getElementById(sourceName)) {
                continue;
            }
            map.addSource(sourceName, {
                type: 'vector',
                url: `http://0.0.0.0:3000/${sourceName}`
            });

            // Create a link.
            const link = document.createElement('a');
            link.id = sourceName;
            link.href = '#';
            link.textContent = sourceName;
            link.title = sourceName;

            // Show or hide layer when the toggle is clicked.
            link.onclick = function (e) {
                e.preventDefault();
                e.stopPropagation();
                const sourceLayers = map.style.sourceCaches[sourceName]._source.vectorLayerIds;
                if (!sourceLayers) {
                    return;
                }
                const isShowing = this.classList.contains('active')
                const color = string2RandColor(sourceName);
                for (const sourceLayer of sourceLayers) {
                    for (const [type, geoType, alpha] of [["circle", "Point", 0.8], ["line", "LineString", 0.8], ["fill", "Polygon", 0.4]]) {
                        const layerId = `${sourceName}_${sourceLayer}_${type}`;
                        if (isShowing) {
                            map.removeLayer(layerId)
                        } else {
                            map.addLayer({
                                id: layerId,
                                source: sourceName,
                                type: type,
                                'source-layer': sourceLayer,
                                filter: ['==', '$type', geoType],
                                paint: {[`${type}-color`]: `rgba(${color},${alpha})`}
                            });
                        }
                    }
                }
                if (isShowing) {
                    this.classList.remove('active');
                    link.style.cssText = '';
                } else {
                    this.classList.add('active');
                    link.style.cssText = `background: rgb(${color});
                                          background: linear-gradient(90deg, rgba(${color},0) 15%, rgba(${color},1) 100%);`;
                }
            };

            const layers = document.getElementById('menu');
            layers.appendChild(link);
        }
    });
</script>
</body>

</html>
